package edu.snake.implement.controller.general;

import edu.snake.core.controller.ArenaController;
import edu.snake.core.model.Arena;
import edu.snake.core.model.Character;
import edu.snake.core.model.Position;
import edu.snake.implement.model.arena.BorderCharacter;

import java.util.Collections;
import java.util.List;

import static edu.snake.implement.factory.PositionFactory.getNewPosition;


public class ArenaControllerImpl implements ArenaController {

    private Arena model;

    public ArenaControllerImpl(Arena model) {
        this.model = model;
    }

    /**
     * Get character from position if character null - return null,
     * then gettred character move to new position if collision not happend:
     * return Collision Character if exist on new position, gettred character - not move.
     * return null if collision not happend and move is ok.
     * return BorderCharacter if new position out of range Arena
     */

    @Override
    public synchronized Character move(Position currentPosition, Direction direction) {
        Character character = model.getFromArena(currentPosition);
        if (character == null) {
            return null;
        }
        Position positionNew = generateNewPosition(currentPosition, direction);
        if (checkBounds(positionNew)) {
            return new BorderCharacter(currentPosition);
        }

        Character collisionCharacter = checkCollision(positionNew);
        if (collisionCharacter == null) {
            character.setPosition(positionNew);
            model.setToArena(currentPosition, null);
            model.setToArena(positionNew, character);
            return null;
        }
        return collisionCharacter;
    }

    /**
     * @param character
     * @param direction
     * @return
     */

    @Override
    public synchronized Character setToArenaWithMove(Character character, Direction direction) {
        Position currentPosition = character.getPosition();
        Position positionNew = generateNewPosition(currentPosition, direction);
        if (checkBounds(positionNew)) {
            return new BorderCharacter(currentPosition);
        }
        Character collisionCharacter = checkCollision(positionNew);
        if (collisionCharacter != null) {
            return collisionCharacter;
        }
        character.setPosition(positionNew);
        model.setToArena(positionNew, character);
        return null;
    }

    /**
     * @return Position
     * if  new position out of bound return null
     */

    protected synchronized Position generateNewPosition(Position position, Direction direction) {
        int width = position.getWidth();
        int height = position.getHeight();
        switch (direction) {
            case LEFT:
                width++;
                break;
            case DOWN:
                height++;
                break;
            case RIGHT:
                width--;
                break;
            default:
                height--;
                break;
        }
        return getNewPosition(width, height);
    }

    /**
     * return null if empty
     * return BorderCharacter if out of arena bounds
     */
    protected synchronized Character checkCollision(Position position) {
        if (checkBounds(position)) {
            return new BorderCharacter(position);
        }
        return this.getFromArena(position);
    }

    private synchronized boolean checkBounds(Position position) {
        if (position.getWidth() < 0 || position.getWidth() >= model.getWidth()) {
            return true;
        }
        if (position.getHeight() < 0 || position.getHeight() >= model.getHeight()) {
            return true;
        }
        return false;
    }

    @Override
    public synchronized Character getFromArena(Position position) {
        return model.getFromArena(position);
    }

    @Override
    public synchronized void setToArena(Position position, Character character) {
        model.setToArena(position, character);
    }

    @Override
    public Arena getModel() {
        return model;
    }

    @Override
    public boolean checkFreeSpace() {
        return  model.checkFreeSpace();
    }

    public List<Position> checkFreeSpaceList() {
        return Collections.emptyList();//todo check free position on arena on perspective
    }
}
